<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vue3 Gridstack: Gridstack DOM with Vue Rendering</title>
  <link rel="stylesheet" href="demo.css" />
  <script src="../dist/gridstack-all.js"></script>
</head>

<body>
  <main id="app">
    <a href="./index.html">Back to All Demos</a>
    <h1>Vue3: Gridstack Controls Vue Rendering Grid Items</h1>
    <p>
      <strong>Use Vue3 render functions with GridStack.renderCB</strong><br />
      GridStack handles widget creation and Vue handles rendering the content using the modern (since V11) GridStack.renderCB.
    </p>
    <p>
      Helpful Resources:
    <ul>
      <li><a href="https://vuejs.org/guide/extras/render-function.html#render-functions-jsx" target="_blank">Vue Render Functions</a></li>
      <li><a href="https://stackblitz.com/edit/vitejs-vite-phhdkeju" target="_blank">This Demo as a Vue SFC on Stackblitz</a></li>
    </ul>
    </p>
    <button type="button" @click="addNewWidget">Add Widget</button> {{ info }}
    <div class="grid-stack"></div>
  </main>
  <script type="module">
    import { createApp, ref, onMounted, onBeforeUnmount, h, render, toRefs } from "https://cdn.jsdelivr.net/npm/vue@3.0.11/dist/vue.esm-browser.js";

    const GridItemComponent = {
      props: {
        itemId: {
          type: [String, Number],
          required: true,
        },
      },
      emits: ['remove'],
      setup(props, { emit }) {
        const { itemId } = toRefs(props)

        onBeforeUnmount(() => {
          console.log(`In vue onBeforeUnmount for item ${itemId.value}`)
        });

        function handleRemove() {
          emit('remove')
        }

        return {
          itemId,
          handleRemove,
        }
      },
      template: `
        <button @click="handleRemove">X</button>
        <p>
            Vue Grid Item {{ itemId }}
        </p>
        `
    }

    createApp({
      setup() {
        let info = ref("");
        let grid = null;
        const items = [
          { id: 1, x: 2, y: 1, h: 2 },
          { id: 2, x: 2, y: 4, w: 3 },
          { id: 3, x: 4, y: 2 },
          { id: 4, x: 3, y: 1, h: 2 },
          { id: 5, x: 0, y: 6, w: 2, h: 2 },
        ];
        let count = ref(items.length);
        const shadowDom = {}

        onMounted(() => {
          grid = GridStack.init({
            float: true,
            cellHeight: "70px",
            minRow: 1,
          });

          // Listen for remove events to clean up Vue renders
          grid.on('removed', function (event, items) {
            items.forEach(item => {
              if (shadowDom[item.id]) {
                render(null, shadowDom[item.id]);
                delete shadowDom[item.id];
              }
            });
          });

          GridStack.renderCB = function (el, widget) {
            // el: HTMLElement div.grid-stack-item-content
            // widget: GridStackWidget

            const gridItemEl = el.closest('.grid-stack-item'); // div.grid-stack-item (parent of el)

            // Create Vue component for the widget content
            const itemId = widget.id
            const widgetNode = h(GridItemComponent, {
              itemId: itemId,
              onRemove: () => { // Catch the remove event from the Vue component
                grid.removeWidget(gridItemEl); // div.grid-stack-item
                info.value = `Widget ${itemId} removed`;
              }
            })
            shadowDom[itemId] = el
            render(widgetNode, el) // Render Vue component into the GridStack-created element
          }

          grid.load(items);
        });

        onBeforeUnmount(() => {
          // Clean up Vue renders
          Object.values(shadowDom).forEach(el => {
            render(null, el)
          })
        });

        function addNewWidget() {
          const node = items[count.value] || {
            x: Math.round(12 * Math.random()),
            y: Math.round(5 * Math.random()),
            w: Math.round(1 + 3 * Math.random()),
            h: Math.round(1 + 3 * Math.random()),
          };
          node.id = String(count.value++);
          grid.addWidget(node);
          info.value = `Widget ${node.id} added`;
        }

        return {
          info,
          addNewWidget,
        };
      },

      watch: {
        info: function (newVal) {
          if (newVal.length === 0) return;

          window.clearTimeout(this.timerId);
          this.timerId = window.setTimeout(() => {
            this.info = "";
          }, 2000);
        },
      },
    }).mount("#app");
  </script>
</body>

</html>